﻿using System.Collections.Generic;
using System;
using NOCSort;

namespace LaserMark
{
    class CameraArea
    {
        public RectangleStruct cameraAreaRect;
        public List<int> elementIndexList = new List<int>();
        public List<RectangleStruct> elementBoundRects = new List<RectangleStruct>();
    }

    class RectangleStruct
    {
        public double lt_x;
        public double lt_y;
        public double rb_x;
        public double rb_y;

        public double c_x;
        public double c_y;
        public double width;
        public double height;
        public RectangleStruct(double x, double y, double w, double h)
        {
            c_x = x;
            c_y = y;
            width = w;
            height = h;

            calcCornerPosition();
        }
        private void calcCornerPosition()
        {
            lt_x = c_x - width / 2.0;
            lt_y = c_y + height / 2.0;
            rb_x = c_x + width / 2.0;
            rb_y = c_y - height / 2.0;
        }
        public bool IsValid()
        {
            if(c_x ==0 && c_y == 0 && width == 0 && height == 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
    }
    class RegionStruct
    {
        public List<int> elements = new List<int>();
        public List<RectangleStruct> elementBoundRects = new List<RectangleStruct>();
        public RectangleStruct regionRect;
        public RegionStruct(int index, double cx, double cy, double width, double height)
        {
            elements.Add(index);
            
            regionRect = new RectangleStruct(cx, cy, width, height);
            elementBoundRects.Add(regionRect);
        }
        public bool EstimateAdd(double cx, double cy, double width, double height, double max_width, double max_height)
        {
            RectangleStruct rs = new RectangleStruct(cx, cy, width, height);

            double min_x = Math.Min(rs.lt_x, regionRect.lt_x);
            double max_x = Math.Max(rs.rb_x, regionRect.rb_x);
            double cw = Math.Abs(max_x - min_x);

            double min_y = Math.Min(rs.rb_y, regionRect.rb_y);
            double max_y = Math.Max(rs.lt_y, regionRect.lt_y);
            double ch = Math.Abs(max_y - min_y);

            if (cw < max_width && ch < max_height)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public void Add(int index, double cx, double cy, double width, double height)
        {
            elements.Add(index);
            RectangleStruct rs = new RectangleStruct(cx, cy, width, height);
            elementBoundRects.Add(rs);

            double min_x = Math.Min(rs.lt_x, regionRect.lt_x);
            double max_x = Math.Max(rs.rb_x, regionRect.rb_x);
            double rcw = Math.Abs(max_x - min_x);
            double rcx = (max_x + min_x) / 2.0;

            double min_y = Math.Min(rs.rb_y, regionRect.rb_y);
            double max_y = Math.Max(rs.lt_y, regionRect.lt_y);
            double rch = Math.Abs(max_y - min_y);
            double rcy = (max_y + min_y) / 2.0;

            regionRect = new RectangleStruct(rcx, rcy, rcw, rch);
        }
    }
    class CameraAreaOptimization
    {
        public static void outputBadmarks(List<BadmarkStructure> badmarks, int[] indexArray)
        {
            Console.WriteLine("-----------------------------------");
            int indexArrayCount = indexArray.Length;
            for(int i =0;i<indexArrayCount; i++)
            {
                Console.WriteLine(string.Format("x:{0}, y:{1}", badmarks[indexArray[i]].X, badmarks[indexArray[i]].Y));
            }
        }

        public static void outputRegions(List<RegionStruct> regions, List<BadmarkStructure> badmarks)
        {
            
            int regionCount = regions.Count;
            for (int i = 0; i < regionCount; i++)
            {
                Console.WriteLine(string.Format("----------------{0}-------------------", i));
                outputBadmarks(badmarks, regions[i].elements.ToArray());
            }
        }

        public static BadmarkStructure generatorNewBadmarkAfterCorrection(BadmarkStructure oldbds)
        {
            double lt_x = oldbds.X - oldbds.SizeW / 2.0;
            double lt_y = oldbds.Y + oldbds.SizeH / 2.0;
            double rb_x = oldbds.X + oldbds.SizeW / 2.0;
            double rb_y = oldbds.Y - oldbds.SizeH / 2.0;

            double _lt_x = lt_x, _lt_y = lt_y, _rb_x = rb_x, _rb_y = rb_y;

            double _min_x = _lt_x, _max_x = _rb_x, _min_y = _rb_y, _max_y = _lt_y;

            double cx = (_min_x + _max_x) / 2.0;
            double cy = (_min_y + _max_y) / 2.0;
            double width = Math.Abs(_min_x - _max_x);
            double height = Math.Abs(_min_y - _max_y);
            BadmarkStructure r = new BadmarkStructure(cx, cy, width, height);
            return r;
        }

        public static CameraArea[] CreateCameraAreasForBadMarkByCameraViewSize(List<BadmarkStructure> badmarks, double mm_width, double mm_height)
        {
            // 创建索引数组
            int indexArrayCount = badmarks.Count;
            int[] badMarkIndexArray = new int[indexArrayCount];
            for (int i = 0; i < indexArrayCount; i++)
            {
                badMarkIndexArray[i] = i;
            }

            // 按照x，y轴的坐标排序

            //      1，先按照x坐标排序
            badMarkIndexArray = CustomSort.MergeSort(badMarkIndexArray, delegate (int index1, int index2) {
                double v1 = badmarks[index1].X;
                double v2 = badmarks[index2].X;
                int r = v1.CompareTo(v2);
                if(r <= 0)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            });
            //CameraAreaOptimization.outputBadmarks(badmarks, badMarkIndexArray);
            //      2，再按照y坐标排序
            badMarkIndexArray = CustomSort.MergeSort(badMarkIndexArray, delegate (int index1, int index2) {
                double v1 = badmarks[index1].Y;
                double v2 = badmarks[index2].Y;
                int r = v1.CompareTo(v2);
                if (r <= 0)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            });
            //CameraAreaOptimization.outputBadmarks(badmarks, badMarkIndexArray);

            // 划分相机区域
            List<RegionStruct> regions = new List<RegionStruct>();
            BadmarkStructure bds = badmarks[badMarkIndexArray[0]];
            BadmarkStructure _bds = generatorNewBadmarkAfterCorrection(bds);
            regions.Add(new RegionStruct(badMarkIndexArray[0], _bds.X, _bds.Y, _bds.SizeW, _bds.SizeH));

            for (int i = 1; i < indexArrayCount; i++)
            {
                bds = generatorNewBadmarkAfterCorrection(badmarks[badMarkIndexArray[i]]);
                int currentMaxElementCount = -1;
                bool findARegion = false;
                RegionStruct maxElementRegion = null;
                for (int j = 0, regionCount = regions.Count; j < regionCount; j++)
                {
                    
                    RegionStruct crs = regions[j];
                    if(crs.EstimateAdd(bds.X, bds.Y, bds.SizeW, bds.SizeH, mm_width, mm_height))
                    {
                        if(crs.elements.Count + 1 > currentMaxElementCount)
                        {
                            currentMaxElementCount = crs.elements.Count + 1;
                            maxElementRegion = crs;
                            findARegion = true;
                        }                        
                    }
                }

                if (findARegion)
                {
                    maxElementRegion.Add(badMarkIndexArray[i], bds.X, bds.Y, bds.SizeW, bds.SizeH);
                }
                else
                {
                    regions.Add(new RegionStruct(badMarkIndexArray[i], bds.X, bds.Y, bds.SizeW, bds.SizeH));
                }
            }
            //outputRegions(regions, badmarks);
            
            CameraArea[] areas = new CameraArea[regions.Count];
            for(int i = 0, regCount = regions.Count; i < regCount; i++)
            {
                CameraArea ca = new CameraArea();
                ca.cameraAreaRect = new RectangleStruct(regions[i].regionRect.c_x, regions[i].regionRect.c_y, mm_width, mm_height);
                regions[i].elements.ForEach(e => ca.elementIndexList.Add(e));
                regions[i].elementBoundRects.ForEach(e => ca.elementBoundRects.Add(e));
                areas[i] = ca;
            }
            int areaCount = areas.Length;
            double splitHeight = mm_height * 0.718;
            // 优化相机走位
            double min_y = 10000;
            double max_y = -10000;
            for(int i = 0; i < areaCount; i++)
            {
                CameraArea ca = areas[i];
                if(ca.cameraAreaRect.rb_y < min_y)
                {
                    min_y = ca.cameraAreaRect.rb_y;
                }

                if(ca.cameraAreaRect.lt_y > max_y)
                {
                    max_y = ca.cameraAreaRect.lt_y;
                }
            }

            double y_stride = Math.Abs(max_y - min_y);
            int split_count = (int)(y_stride / splitHeight) + 1;

            List<int>[] bucket = new List<int>[split_count];
            for (int i = 0; i < split_count; i++)
            {
                bucket[i] = new List<int>();
            }

            for (int i = 0; i < areaCount; i++)
            {
                CameraArea ca = areas[i];
                int index = (int)((ca.cameraAreaRect.c_y - min_y) / splitHeight);
                bucket[index].Add(i);
            }


            CameraArea[] areasSorted = new CameraArea[areaCount];
            int current_index = 0;
            int direct = 1;
            for (int i = 0; i < split_count; i++)
            {
                if (bucket[i].Count > 0)
                {
                    int[] indexArray = new int[bucket[i].Count];
                    for (int j = 0; j < indexArray.Length; j++)
                    {
                        indexArray[j] = j;
                    }

                    indexArray = CustomSort.MergeSort(indexArray, delegate (int index1, int index2) {
                        int area_index1 = bucket[i][index1];
                        int area_index2 = bucket[i][index2];

                        double v1 = areas[area_index1].cameraAreaRect.c_x;
                        double v2 = areas[area_index2].cameraAreaRect.c_x;
                        int r = v1.CompareTo(v2);
                        if (r <= 0)
                        {
                            return true;
                        }
                        else
                        {
                            return false;
                        }
                    });

                    if(direct == 1)
                    {
                        for (int k = 0; k < indexArray.Length; k++)
                        {
                            areasSorted[current_index] = areas[bucket[i][indexArray[k]]];
                            current_index++;
                        }

                    }
                    else
                    {
                        for (int k = indexArray.Length-1; k >= 0; k--)
                        {
                            areasSorted[current_index] = areas[bucket[i][indexArray[k]]];
                            current_index++;
                        }
                    }

                    direct = direct * -1;
                }
            }
            // 构造相机区域返回值
            return areasSorted;
        }
    }
}
